//
//  RandomTree.h
//  RandomForest-AndreaBalboni
//
//  Created by Andrea Balboni on 05/04/12.
//  Copyright (c) 2012 Università degli Studi di Modena e Reggio Emilia. All rights reserved.
//

#ifndef RandomForest_AndreaBalboni_RandomTree_h
#define RandomForest_AndreaBalboni_RandomTree_h

#include "TreeNode.h"
#include <opencv2/opencv.hpp>
#include <map>
#include <iostream>
#include <vector>

//#define THRESH_MINDIFF 4
//#define THRESH_FISHER 0
//#define THRESH_RANDOM 1
//#define THRESH_GINI 2
#define THRESH_IG 3
enum Algorithm {THRESH_RANDOM, THRESH_FISHER, THRESH_GINI};
template <typename _T>
class RandomTree {
    typedef typename TrainingData<_T>::VectorRecordIterator DataSetIterator;
private:
    TreeNode<_T> *_root;
    TrainingData<_T> _DataSet;

    unsigned int _featureSpaceDimension;

    
    //Calcola una soglia a Random tra un valore massimo e minimo
	_T thresholdRandom(_T valMax, _T valMin);
    
    //Calcola la media dei records dati gli indici dei record della classe
     void classMean(std::vector<unsigned int> &recordIndexes, TrainingData<_T> &dataSet, cv::Mat_<double> &mean);
    
    //Calcola il coefficiente di rayleigh e il vettore di proiezione
     void rayleigh(std::vector<unsigned int> &classRecordIndexes, std::vector<unsigned int> &otherClassesRecordIndexes, TrainingData<_T> &dataSet, cv::Mat_<double> &mean1, cv::Mat_<double> &mean2, cv::Mat_<double> &w, double &rayleigh);
    
    //Esegue lo split in calcolando la soglia in modo random
     void splitRandom(TreeNode<_T>* node, TrainingData<_T>& dataSet, int height);
    //Esegue lo split in calcolando la soglia in modo random
     void splitRandom(TreeNode<_T>* node, TrainingData<_T>& dataSet);
    //Esegue lo split in calcolando la soglia in modo da minimizzare la differenza tra il numero di record dei figli
    void splitMinDiff(TreeNode<_T>* node, TrainingData<_T>& dataSet, int height);
    //Esegue lo split in calcolando la soglia in modo da minimizzare la differenza tra il numero di record dei figli
    void splitMinDiff(TreeNode<_T>* node, TrainingData<_T>& dataSet);

    //Calcola la distanza minima tra il numero di record che andranno a destra e sinistra e il rispettivo valore di soglia
//    void minDiffSplitValue(TreeNode<_T> *node, TrainingData<_T> &dataSet, unsigned int splitIndex, _T valMin, _T valMax, std::pair<float,_T>& minDistElem, float &avgDist);
//    void minDiffSplit(TreeNode<_T> *node, TrainingData<_T> &dataSet, unsigned int &splitIndex, _T &splitValue);
    
     void splitGini(TreeNode<_T> *node, TrainingData<_T> &dataSet, int height);
     void splitGini(TreeNode<_T> *node, TrainingData<_T> &dataSet);
    
//    void splitIG(TreeNode<_T> *node, TrainingData<_T> &dataSet);
//    void splitIG(TreeNode<_T> *node, TrainingData<_T> &dataSet, int height);
    
    void doSplit(TreeNode<_T>* node, TrainingData<_T> &dataSet, unsigned int method);
    
     void splitFisher(TreeNode<_T>* node, TrainingData<_T> &dataSet);
     void splitFisher(TreeNode<_T>* node, TrainingData<_T> &dataSet, int height);
    
     void giniIndexSplit(TreeNode<_T> * node, TrainingData<_T> &dataSet, unsigned int &splitIndex, _T &splitValue, unsigned int method);
    
    //Ottiene l'istogramma delle classi per i record del nodo
     void getNodeClassMap(std::vector<unsigned int> &indexes, TrainingData<_T> &DataSet, std::map<int, float> &classMap);
public:

    RandomTree():_featureSpaceDimension(0){}
    
    ~RandomTree(){}
    
    //Imposta la dimensionalita' delle features del dataset considerato
    void setFeatureSpaceDimension(unsigned int featureSpaceDimension);
    
    //Restituisce la dimensionalita' delle features del dataset considerato
    unsigned int getFeatureSpaceDimension();
    
    //Esegue il training di un albero data un'altezza dell'albero
     void trainTree(TrainingData<_T> &dataSet, int height, unsigned int method);

    //Esegue il training di un albero con altezza automatica
     void trainTree(TrainingData<_T> &dataSet, unsigned int method);
    
    //Esegue la classificazione di un record
     unsigned int classify(MyRecord<_T> &record);
    
    //Esegue la classificazione di fisher dato un record in una matrice
     unsigned int classifyFisher(cv::Mat_<double> &record);
};

#endif
